package gang.org.springframework.framework.metadata;

import gang.org.springframework.framework.core.annotation.GangAliasFor;
import gang.org.springframework.framework.core.annotation.GangAnnotationsScanner;
import gang.org.springframework.framework.util.GangReflectionUtils;
import gang.org.springframework.framework.util.GangStringUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @author gang.chen
 * @since 2021/7/1 8:26
 */
final class GangAnnotationTypeMapping
{

    private final int distance;
    private final int[] aliasMappings;

    private final Annotation annotation;
    /**
     * 以ComponentScan注解为参考
     * 1）根注解 ->SpringBootApplication - > source
     * 2) this ->ComponentScan -> this
     **/
    private final GangAnnotationTypeMapping source;
    private final GangAnnotationTypeMapping root;
    private final Class<? extends Annotation> annotationType;
    private final GangAttributeMethods attributes;

    private final Map<Method, List<Method>> aliasedBy;

    /**
     * @param source value is null
     * @param annotationType {@link gang.org.springframework.boot.autoconfigure.GImport}
     * @param annotation value is null
     * */
    GangAnnotationTypeMapping(GangAnnotationTypeMapping source,
                              Class<? extends Annotation> annotationType,
                              Annotation annotation)
    {
        this.source = source;
        this.root = source != null ? source.getRoot() : this;

        this.distance = source == null ? 0 : getDistance() + 1;

        this.annotationType = annotationType;
        this.annotation = annotation;
        this.attributes = GangAttributeMethods.forAnnotationType(annotationType);

        this.aliasMappings = filledIntArray(this.attributes.size());

        this.aliasedBy = resolveAliasedForTargets();

        /**
         * 子注解与父注解，注解映射，通过下标来实现
         * */
        processAliases();
    }

    private int[] filledIntArray(int size) {
        int[] array = new int[size];
        Arrays.fill(array, -1);
        return array;
    }

    private void processAliases() {

        List<Method> aliases = new ArrayList<>();
        /**
         * attributes 数组集合存储了 ComponentScan注解中所有属性（方法）
         * */
        for (int i = 0; i < this.attributes.size(); i++) {
            aliases.clear();
            aliases.add(this.attributes.get(i));
            collectAliases(aliases);

            if (aliases.size() > 1) {

                processAliases(i, aliases);
            }
        }
    }

    private void processAliases(int attributeIndex, List<Method> aliases) {

        int rootAttributeIndex = getFirstRootAttributeIndex(aliases);

        GangAnnotationTypeMapping mapping = this;

        while (mapping != null) {

            if (rootAttributeIndex != -1 && mapping != this.root) {

                //子注解方法：ComponentScan.basePackages() 与 父注解方法： SpringBootApplication.scanBasePackages() 建立映射

                for (int i = 0; i < mapping.attributes.size(); i++)
                {
                    if (aliases.contains(mapping.attributes.get(i)))
                    {
                        mapping.aliasMappings[i] = rootAttributeIndex;
                    }
                }

            }

            //TODO

            //TODO

            //TODO

            mapping = mapping.source;
        }
    }

    private int getFirstRootAttributeIndex(List<Method> aliases) {

        GangAttributeMethods rootAttributes = this.root.getAttributes();

        for (int i = 0; i < rootAttributes.size(); i++) {

            if (aliases.contains(rootAttributes.get(i))) {
                return i;
            }
        }

        return -1;
    }

    /**
     * @param aliases 别名集合，其对应的方法是-ComponentScan注解方法集合，从第一个方法到第N个方法
     * 每一个方法，都需要在“AnnotationTypeMapping(ComponentScan), AnnotationTypeMapping(SpringBootApplication)” 两个注解类中查收，由底向上
     * */
    private void collectAliases(List<Method> aliases) {
        /**
         * AnnotationTypeMapping 实例是对 ComponentScan的封装
         * */
        GangAnnotationTypeMapping mapping = this;

        while (mapping != null) {

            int size = aliases.size();

            for (int j = 0; j < size; j++)
            {
                /**
                 * aliasedBy 数据结构显示+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                 * Map(Method, ArrayList) -> Map(ComponentScan.basePackages(), ComponentScan.value())
                                          -> Map(ComponentScan.value(), ComponentScan.basePackages())
                 * aliasedBy 数据结构显示+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                 * */
                List<Method> additional = mapping.aliasedBy.get(aliases.get(j));

                if (additional != null) {
                    aliases.addAll(additional);
                }
            }
            /**
             * AnnotationTypeMapping(ComponentScan) -> AnnotationTypeMapping(SpringBootApplication)
             * aliasedBy数据结构显示++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
             * Map(Method, ArrayList) -> Map(ComponentScan.basePackages(), SpringBootApplication.scanBasePackages())
             *                           Map(ComponentScan.nameGenerator(), SpringBootApplication.nameGenerator())
             *                           Map(ComponentScan.basePackageClasses(), SpringBootApplication.scanBasePackageClasses)
             *                           Map(Configuration.proxyBeanMethods(), SpringBootApplication.proxyBeanMethods())
             *                           Map(EnableAutoConfiguration.excludeName(), SpringBootApplication.excludeName())
             *                           Map(EnableAutoConfiguration.exclude(), SpringBootApplication.exclude())
             * aliasedBy数据结构显示++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
             * */
            mapping = mapping.source;
        }
    }

    /**
     * 业务意义：
     * 1：抽象说明：对所有注解(E.g SpringBootApplication, ComponentScan等)互为别名进行提取，存储于Map中
     * 2：实例说明：注解ComponentScan，ComponentScan.value & ComponentScan.basePackages互为别名注解属性名进行提取，存储于Map中
     * */
    private Map<Method, List<Method>> resolveAliasedForTargets() {
        Map<Method, List<Method>> aliasedBy = new HashMap<>();

        for (int i = 0; i < this.attributes.size(); i++) {

            Method method = this.attributes.get(i);

            GangAliasFor aliasFor = GangAnnotationsScanner.getDeclaredAnnotation(method, GangAliasFor.class);

            if (aliasFor != null) {

                /**
                 * 业务意义
                 * input -> ComponentScan.value
                 * output-> ComponentScan.basePackages
                 * */
                Method target = resolvedAliasTarget(method, aliasFor);

                aliasedBy.computeIfAbsent(target, key -> new ArrayList<>()).add(method);
            }

        }

        return Collections.unmodifiableMap(aliasedBy);
    }

    private Method resolvedAliasTarget(Method method, GangAliasFor aliasFor) {
        return resolveAliasTarget(method, aliasFor, true);
    }

    /***
     * 该方法的业务意义在于
     * input  -> ComponentScan.value
     * output -> ComponentScan.basePackages
     * */
    private Method resolveAliasTarget(Method method, GangAliasFor aliasFor, boolean checkAliasPair) {

        if (false){
            //TODO
        }

        Class<? extends Annotation> targetAnnotation = aliasFor.annotation();

        if (targetAnnotation == Annotation.class){
            targetAnnotation = this.annotationType;
        }

        //获取注解属性值attribute值 attribute == ""
        String targetAttributeName = aliasFor.attribute();
        if (!GangStringUtils.hasLength(targetAttributeName)) {
            //为空值则取value属性值 value = "value"
            targetAttributeName = aliasFor.value();
        }
        if (false){
            //TODO
        }

        //获取方法为：ComponentScan-> value()
        Method target = GangAttributeMethods.forAnnotationType(targetAnnotation).get(targetAttributeName);

        if (target == null) {
            //TODO nothing
        }

        if (target.equals(method)){
            //TODO nothing
        }

        //返回数据，格式验证
        if (false){
            //TODO nothing
        }
        if (isAliasPair(target) && checkAliasPair){

            GangAliasFor targetAliasFor = target.getAnnotation(GangAliasFor.class);

            if (targetAliasFor != null) {

                resolveAliasTarget(target, targetAliasFor, false);
            }
        }
        return target;
    }

    Class<? extends Annotation> getAnnotationType(){
        return this.annotationType;
    }

    public GangAttributeMethods getAttributes() {
        return attributes;
    }

    public GangAnnotationTypeMapping getSource() {
        return this.source;
    }

    GangAnnotationTypeMapping getRoot(){
        return this.root;
    }

    private boolean isAliasPair(Method target)
    {
        return this.annotationType == target.getDeclaringClass();
    }

    int getAliasMapping(int attributeIndex)
    {
        return this.aliasMappings[attributeIndex];
    }

    int getDistance()
    {
        return this.distance;
    }

    Object getMappedAnnotationValue(int attributeIndex, boolean metaAnnotationsOnly)
    {
        int mappedIndex = attributeIndex;

        if (mappedIndex == -1) {
            //TODO
            return null;
        }
        if (false){
            //TODO
            return null;
        }

        return GangReflectionUtils.invokeMethod(source.attributes.get(mappedIndex), source.annotation);
    }

    public Annotation getAnnotation() {
        return annotation;
    }
}
